package co.touchlab.skie.buildsetup.main.tasks

import co.touchlab.skie.buildsetup.main.tasks.GeneratePrimarySmokeTestsCIActionTask.LibrariesTestBatch
import co.touchlab.skie.buildsetup.util.version.SupportedKotlinVersion
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import kotlin.io.path.createDirectories
import kotlin.io.path.deleteIfExists
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.name
import kotlin.io.path.writeText

abstract class GenerateVersionedSmokeTestsCIActionsTask : DefaultTask() {

    @get:Input
    abstract val supportedVersions: ListProperty<SupportedKotlinVersion>

    @get:InputDirectory
    abstract val libraryTestResources: DirectoryProperty

    @get:OutputDirectory
    abstract val outputDirectory: RegularFileProperty

    init {
        group = "other"
        description = "Generates the versioned smoke-tests workflow files."
    }

    @TaskAction
    fun execute() {
        val testResources = libraryTestResources.get().asFile

        val outputPath = outputDirectory.get().asFile.toPath()

        outputPath.createDirectories()

        outputPath.listDirectoryEntries()
            .filter { it.name.startsWith("smoke-tests-") && it.name["smoke-tests-".length].isDigit() }
            .forEach { it.deleteIfExists() }

        supportedVersions.get().forEach {
            val librariesTestBatches = LibrariesTestBatch.allFrom(it.name.toString(), testResources)

            val selfHostedWorkflow = getSmokeTestsWorkflow(it, librariesTestBatches, selfHosted = true)
            val githubHostedWorkflow = getSmokeTestsWorkflow(it, librariesTestBatches, selfHosted = false)

            outputPath.resolve("smoke-tests-${it.safeName}-self-hosted.yml").writeText(selfHostedWorkflow)
            outputPath.resolve("smoke-tests-${it.safeName}-github.yml").writeText(githubHostedWorkflow)
        }
    }

    private fun getSmokeTestsWorkflow(version: SupportedKotlinVersion, librariesTestBatches: List<LibrariesTestBatch>, selfHosted: Boolean): String {
        val versionWithCompilerVersion = $$"$${version.name}[${{ inputs.compiler_version || '$${version.name}' }}]"

        val runner = if (selfHosted) "self-hosted" else "macos-14"

        return $$"""
            name: Smoke Tests $${version.name} [$${if (selfHosted) "self-hosted" else "github-hosted"}]

            # Do not edit - generated by `generateCIActions` Gradle task.

            on:
            """.trimIndent() +

            if (selfHosted) {
                """

                  schedule:
                    # Every Saturday at 12am EST
                    - cron: '0 5 * * SAT'
                   """.trimIndent().prependIndent("  ")
            } else {
                ""
            } +

            $$"""

              workflow_dispatch:
                inputs:
                  suites:
                    type: string
                    required: false
                    default: 'acceptance,type-mapping,gradle,$${librariesTestBatches.joinToString(",") { "lib-${it.range}" }}'
                    description: |
                      Comma-separated test suites to run. Available: acceptance, type-mapping, gradle, $${librariesTestBatches.joinToString(", ") { "lib-${it.range}" }}
                      Leave empty to run all. Use '!' prefix to exclude (e.g., '!lib-0-499').
                  compiler_version:
                    type: string
                    required: true
                    default: '$${version.name}'
                    description:
                      'The Kotlin compiler version used by the tests.'
                  linkage:
                    type: choice
                    options:
                      - static
                      - dynamic
                    required: true
                    default: static
                    description:
                      'The linkage mode to use for the tests. "static" will produce static frameworks, "dynamic" will produce dynamic frameworks.'
                  configuration:
                    type: choice
                    options:
                      - debug
                      - release
                    required: true
                    default: debug
                    description:
                      'The configuration to use for the tests. "release" will produce release builds, "debug" will produce debug builds (type mapping tests currently always use release).'
                  target:
                    type: choice
                    options:
                      - ios_arm64
                      - ios_x64
                      - ios_simulator_arm64
                      - macos_arm64
                      - macos_x64
                    required: true
                    default: macos_arm64
                    description:
                      'The target to use for the type mapping tests.'

            permissions:
              contents: read
              checks: write

            jobs:
              acceptance-tests:
                name: Acceptance Tests $$versionWithCompilerVersion
                if: |-
                  (!inputs.suites || (contains(inputs.suites, 'acceptance') && !contains(inputs.suites, '!acceptance')))
                runs-on: $$runner
                steps:
                  - name: Checkout Repo
                    uses: actions/checkout@v3
                    with:
                      submodules: true
                      token: ${{ secrets.ACCEPTANCE_TESTS_TOKEN }}
                  - name: Prepare Worker
                    uses: ./.github/actions/prepare-worker
                  - name: Run Acceptance Tests
                    uses: gradle/gradle-build-action@v2.4.2
                    with:
                      arguments: ":acceptance-tests:functional:test -PversionSupport.kotlin.enabledVersions=$$versionWithCompilerVersion"
                      build-root-directory: SKIE
                    env:
                      KOTLIN_LINK_MODE: ${{ inputs.linkage }}
                      KOTLIN_BUILD_CONFIGURATION: ${{ inputs.configuration }}

              gradle-tests:
                name: Gradle Tests $$versionWithCompilerVersion
                if: |
                  (!inputs.suites || (contains(inputs.suites, 'gradle') && !contains(inputs.suites, '!gradle')))
                runs-on: $$runner
                steps:
                  - name: Checkout Repo
                    uses: actions/checkout@v3
                    with:
                      submodules: true
                      token: ${{ secrets.ACCEPTANCE_TESTS_TOKEN }}
                  - name: Prepare Worker
                    uses: ./.github/actions/prepare-worker
                  - name: Run Gradle Tests
                    uses: gradle/gradle-build-action@v2.4.2
                    id: run-tests
            #      TODO The targets cannot be selected at the moment due to a mismatch in the name used by Gradle and other kinds of tests
            #      "-Pmatrix.targets=${{ inputs.target || 'macosArm64' }}"
                    with:
                      arguments: >-
                        :test
                        -PtestLevel=smoke
                        -PtestType=gradle
                        "-Pmatrix.targets=macosArm64"
                        "-Pmatrix.configurations=${{ inputs.configuration || 'debug' }}"
                        "-Pmatrix.linkModes=${{ inputs.linkage || 'static' }}"
                        "-PversionSupport.kotlin.enabledVersions=$$versionWithCompilerVersion"
                      build-root-directory: test-runner
                  - name: Publish Test Report
                    uses: mikepenz/action-junit-report@v4
                    if: ${{ failure() || success() }}
                    with:
                      check_name: "Smoke Test Reports - Gradle Tests"
                      report_paths: 'test-runner/build/test-results/test/TEST-*.xml'
                      require_tests: true

              type-mapping-tests:
                name: Type Mapping Tests $$versionWithCompilerVersion
                if: |
                  (!inputs.suites || (contains(inputs.suites, 'type-mapping') && !contains(inputs.suites, '!type-mapping')))
                runs-on: $$runner
                steps:
                  - name: Checkout Repo
                    uses: actions/checkout@v3
                    with:
                      submodules: true
                      token: ${{ secrets.ACCEPTANCE_TESTS_TOKEN }}
                  - name: Prepare Worker
                    uses: ./.github/actions/prepare-worker
                  - name: Run Type Mapping Tests
                    uses: gradle/gradle-build-action@v2.4.2
                    id: run-tests
                    with:
                      arguments: ":acceptance-tests:type-mapping:test -PversionSupport.kotlin.enabledVersions=$$versionWithCompilerVersion"
                      build-root-directory: SKIE
                    env:
                      KOTLIN_LINK_MODE: ${{ inputs.linkage }}
                      KOTLIN_TARGET: ${{ inputs.target }}
                      KOTLIN_BUILD_CONFIGURATION: ${{ inputs.configuration }}

            """.trimIndent() +
            librariesTestBatches.joinToString(System.lineSeparator()) {
                getExternalLibrariesJob(it, versionWithCompilerVersion, runner).prependIndent("  ")
            } + "\n"
    }

    private fun getExternalLibrariesJob(
        testBatch: LibrariesTestBatch,
        versionName: String,
        runner: String,
    ): String = $$"""

          external-libraries-tests-$${testBatch.range}:
            name: External Libraries Tests ($${versionName}) $${testBatch.range}
            if: |
              (!inputs.suites || (contains(inputs.suites, 'lib-$${testBatch.range}') && !contains(inputs.suites, '!lib-$${testBatch.range}')))
            runs-on: $$runner
            steps:
              - name: Checkout Repo
                uses: actions/checkout@v3
                with:
                  submodules: true
                  token: ${{ secrets.ACCEPTANCE_TESTS_TOKEN }}
              - name: Prepare Worker
                uses: ./.github/actions/prepare-worker
              - name: Run External Libraries Tests
                uses: gradle/gradle-build-action@v2.4.2
                with:
                  arguments: ":acceptance-tests:libraries:test -PversionSupport.kotlin.enabledVersions=$${versionName}"
                  build-root-directory: SKIE
                env:
                  KOTLIN_LINK_MODE: ${{ inputs.linkage }}
                  KOTLIN_BUILD_CONFIGURATION: ${{ inputs.configuration }}
                  onlyIndices: "$${testBatch.range}"
        """.trimIndent()
}
